`timescale 1ns / 1ps
/*--------------------------------------------------------------------*\
     FileName        : cbb_spi_master.v
     Author          ：hpy
     Email           ：yuan_hp@qq.com
     Date            ：2022年07月25日
     Description     ：spi master基本模块

1.spcr0 ,控制寄存器
  spcr0[0]：设置cpol值
  spcr0[1]：设置cpha值
  spcr0[2]：spi使能， 该信号为低电平时清空fifo，且spi停止工作
  spcr0[3]：为1时，若spi在idel状态即便检测到fifo非空也不发送数据
2.spcr1 ,控制寄存器，为对时钟的 spcr1+1 次计数，到点产生脉冲信号
\*--------------------------------------------------------------------*/
module cbb_spi_master #(
        parameter dw =8  ,//数据宽度
        parameter de =8   //数据深度，verilog根据设置值，自动向上修正为2^n值
)(
        input clk,
        input rst_n,
        input [7:0]spcr0 , // 配置SPI模式  spcr0[3]:sppause  spcr0[2]:spe  spcr0[1]:cpha spcr0[0]:cpol   
        input [7:0]spcr1 ,  //配置SPI时钟分频

        input [7:0]dat_i , // 
        input wfwe_i , 
        input rfre_i , 
        output [7:0] dat_o, 
        output wffull_o,
        output rfempty_o,

        output reg tirq ,
        //spi
        input miso_i,
        output mosi_o,
        output reg sck_o,
        output  cs_o
);
 
reg [7:0] treg ; 
reg cs_r ; 

assign cs_o = cs_r ; 

wire cpol , cpha, spe ; 
wire sppause ; //

assign cpol = spcr0[0] ; 
assign cpha = spcr0[1] ; 
assign spe  = spcr0[2] ; 
assign sppause = spcr0[3] ;

wire [7:0] wfdout ;
reg wfre ; 
wire wffull , wfempty ; 
assign wffull_o = wffull ;
cbb_fifo #(
	.de(de),  //数据深度，任意正整数，若不是2的幂，会向上自动取2的幂次值，如设置为3，那么实际深度为4
  .dw(dw)    //数据宽度，
)
wfifo(
	.clk   ( clk   ),
	.rst_n   ( rst_n   ),
	.clr   ( ~spe    ),
	.din   ( dat_i   ),
	.we    ( wfwe_i    ),
	.dout  ( wfdout  ),
	.re    ( wfre    ),
	.full  ( wffull  ),
	.empty ( wfempty )
);


reg rfwe ; 
wire rfre ;
wire rffull ;
cbb_fifo #(
	.de(de),  //数据深度，任意正整数，若不是2的幂，会向上自动取2的幂次值，如设置为3，那么实际深度为4
  .dw(dw)    //数据宽度，
)
rfifo(
    .clk   ( clk  ),
    .rst_n   ( rst_n   ),
    .clr   ( ~spe    ),
    .din   ( treg    ),
    .we    ( rfwe    ),
    .dout  ( dat_o  ),
    .re    ( rfre_i    ),
    .full  ( rffull  ),
    .empty ( rfempty_o )
) ;


//
reg [ 7:0 ] clkcnt ; 
wire ena ; 
assign ena = ~|clkcnt ; 
always@(posedge clk)
begin
        if(spe & (|clkcnt & |state ))begin
                clkcnt <= ( |clkcnt ) ? clkcnt-1'b1 : spcr1 ;
        end
        else begin
                clkcnt <= spcr1 ; 
        end
end


reg [1:0]state ; 
reg [3:0] bcnt ; 
always@(posedge clk ) begin
        if(~spe)begin 
              state <= 2'b00 ;  
              bcnt  <= 3'h0;
              wfre  <= 1'b0;
              rfwe  <= 1'b0;
              cs_r  <= 1'b1 ; 
              tirq  <= 1'b0;
        end
        else begin 
                wfre <= 1'b0;
                rfwe <= 1'b0;
                tirq <= 1'b0;

                case(state)
                        2'b00: // idel
                          begin 
                                bcnt  <= 3'd7 ; //
                                treg  <= wfdout ; 
                                sck_o <= cpol ; 
                                cs_r  <= 1'b1 ;
                                
                                // if(~wfempty)begin
                                //     bcnt  <= 3'd7 ; 
                                //     wfre  <= 1'b1;
                                //     state <= 2'b01;
                                //     if(cpha) sck_o <= ~sck_o;
                                //     cs_r <= 1'b0;
                                // end
                                 if(~wfempty & ~sppause)begin
                                    state <= 2'b10;
                                    //if(cpha) sck_o <= ~sck_o;
                                    cs_r <= 1'b0;
                                end  
                          end
                        
                        2'b01: // 
                            if(ena) begin 
                                sck_o <= ~sck_o ; 
                                state <= 2'b11;
                            end

                        2'b11: //       
                            if(ena) begin
                                treg <= {treg[6:0],miso_i} ; 
                                bcnt <= bcnt -3'h1 ; 

                                if(~|bcnt)begin                            
                                    sck_o  <= cpol ;
                                    rfwe   <= 1'b1 ; 
                                    bcnt   <= 3'h7;
                                    if(~wfempty)begin
                                      state  <=  2'b01 ; 
                                      cs_r <= 1'b0;
                                      treg  <= wfdout ;
                                      if(cpha) sck_o <= ~sck_o; 
                                      wfre  <= 1'b1;
                                    end
                                    else begin
                                      cs_r <= 1'b0;
                                      state  <=  2'b10 ; 
                                    end
                                end else begin
                                    state  <= 2'b01 ; 
                                    sck_o  <= ~sck_o; 
                                end
                            end  

                        2'b10 : begin
                          if(ena)begin 
                              if(~wfempty)begin
                                  bcnt  <= 3'd7 ; 
                                  treg  <= wfdout ; 
                                  wfre  <= 1'b1;
                                  state <= 2'b01;
                                  if(cpha) sck_o <= ~sck_o;
                                  cs_r <= 1'b0;
                              end else begin 
                                  cs_r <= 1'b1;
                                  state <= 2'b00;
                                  tirq <= 1'b1; 
                              end
                          end 
                        end
                                         
                endcase
        end 
end 

assign mosi_o  = treg[7]  ; 


endmodule
 
